package com.hsu.yupao.job;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hsu.yupao.model.domain.Chat;
import com.hsu.yupao.model.domain.Team;
import com.hsu.yupao.model.domain.User;
import com.hsu.yupao.model.vo.TeamUserVO;
import com.hsu.yupao.service.ChatService;
import com.hsu.yupao.service.TeamService;
import com.hsu.yupao.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 定时任务类，用于预缓存推荐用户列表。
 * 旨在减少高峰时段数据库的压力，提高用户获取推荐列表的响应速度。
 * @ClassName: PreCacheJob
 * @Author: Hsu琛君珩
 * @Date: 2024-05-17
 * @Version: v1.0
 */
@Component
@Slf4j
public class PreCacheJob {

    @Resource
    private UserService userService;

    @Resource
    private TeamService teamService;

    @Resource
    private ChatService chatService;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 重点用户列表，目前包含一个用户ID为7的示例
    private List<Long> mainUserList = Arrays.asList(7L);

    /**
     * 定时任务，每天2点30分执行，为重点用户预缓存推荐列表。
     * 默认查询前20个用户并设置30秒的缓存过期时间。
     */
    @Scheduled(cron = "0 30 2 * * *")
    public void doCacheRecommendUser() {
        // 获取分布式锁，防止多个任务同时执行
        RLock lock = redissonClient.getLock("youyuan:precache:recommendUsers");
        try {
            // 尝试立即获取锁，如果成功获取到锁，则执行下面的代码块；如果获取失败，则不进行等待，直接执行后续的逻辑
            if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                System.out.println("getLock: " + Thread.currentThread().getId());
                // 遍历重点用户列表，为每个用户预缓存推荐列表
                for (Long userId : mainUserList) {
                    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
                    // 查询数据库获取用户列表
                    Page<User> userPage = userService.page(new Page<>(1, 20), queryWrapper);
                    String redisKey = String.format("youyuan:user:recommend:%s", userId);
                    ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
                    // 尝试写入缓存，缓存期限设置为30秒
                    try {
                        valueOperations.set(redisKey, userPage, 30000, TimeUnit.MILLISECONDS);
                    } catch (Exception e) {
                        // 记录写入缓存时出现的异常
                        log.error("Failed to set Redis key", e);
                    }
                }
            }
        } catch (InterruptedException e) {
            // 记录获取锁时出现的异常
            log.error("Failed to acquire lock", e);
        } finally {
            // 释放锁
            if (lock.isHeldByCurrentThread()) {
                System.out.println("unlock: " + Thread.currentThread().getId());
                lock.unlock();
            }
        }
    }

    /**
     * 每天凌晨2:20执行的定时任务，用于缓存所有队伍信息。
     * 通过Redisson分布式锁确保任务不会在多个实例中重复执行。
     * 任务成功获取锁后，从数据库获取所有队伍信息并存储至Redis中，缓存时长设为24小时。
     * 此操作有助于减轻高峰时段的数据库压力。
     */
    @Scheduled(cron = "0 20 2 * * *")
    public void cacheAllTeams() {
        RLock lock = redissonClient.getLock("youyuan:precache:allTeams");
        try {
            if (lock.tryLock(0, -1, TimeUnit.MINUTES)) {
                // 获取所有队伍的信息
                List<TeamUserVO> teams = teamService.listTeams(null, true);
                // 将获取到的队伍信息存储到Redis，有效期设置为一天
                redisTemplate.opsForValue().set("yupao:teams:info", teams, 24, TimeUnit.HOURS);
            }
        } catch (InterruptedException e) {
            // 处理可能的中断异常
            Thread.currentThread().interrupt();
            throw new IllegalStateException("Failed to lock the resource", e);
        } finally {
            // 确保锁一定会被释放
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 每7天执行一次，清理所有超过7天的聊天记录。
     * 该任务通过Redisson分布式锁确保不会重复执行。
     * 清理操作涉及数据库的删除操作，确保聊天记录数据的及时清理，以节省空间和维护数据健康。
     */
    @Scheduled(cron = "0 0 0 */7 * ?")
    public void clearOldChatRecords() {
        RLock lock = redissonClient.getLock("youyuan:precache:cleanup");
        try {
            // 尝试获取锁
            if (lock.tryLock(0, -1, TimeUnit.MINUTES)) {
                Date sevenDaysAgo = DateUtil.offsetDay(new Date(), -7);
                LambdaQueryWrapper<Chat> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.lt(Chat::getCreateTime, sevenDaysAgo);
                boolean removed = chatService.remove(queryWrapper);
                if (!removed) {
                    log.error("Failed to remove old chat records");
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("Thread was interrupted during chat records cleanup", e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

}
